package net.butfly.bus.client;

import net.butfly.bus.Request;
import net.butfly.bus.Response;
import net.butfly.bus.client.connection.Connection;
import net.butfly.bus.client.connection.ConnectionFactory;
import net.butfly.bus.context.Constants;
import net.butfly.bus.exception.BusRuntimeException;
import net.butfly.bus.filter.FilterBase;
import net.butfly.bus.filter.FilterChain;
import net.butfly.bus.router.Router;
import net.butfly.bus.util.LogUtils;

class ClientInvoker {
	private static LogUtils logger = LogUtils.getLog();
	private FilterChain filterChain;
	private final FilterBase filterBase = new ClientBase();

	public Response invoke(Request request) {
		Response response = new Response();

		long now = System.currentTimeMillis();

		if (request == null) { throw new BusRuntimeException(Constants.UserError.BAD_REQUEST,
				"request should not be null."); }
		if (request.getTx() == null || "".equals(request.getTx().trim())) { throw new BusRuntimeException(
				Constants.UserError.BAD_REQUEST, "tx code should not be empty."); }

		logger.debug(LogUtils.getInstance().getFlowNo(Constants.getFlowNo(request.getTx(), request.getVersion()))
				+ " bus client starting...");

		try {
			if (null == this.filterChain) this.filterChain = new FilterChain(ClientConfig.getFilterBeanList());
			filterChain.execute(request, response, filterBase);
		} catch (BusRuntimeException ex) {
			response.setExecuteFail();
			response.setErrorCode(ex.getCode());
			response.setErrorMessage(ex.getMessage());
			response.setErrorStack(ex);
			logger.error(LogUtils.getInstance().getFlowNo() + " runtime error: ", ex);
		} catch (Exception ex) {
			response.setExecuteFail();
			response.setErrorCode(Constants.SystemError.UNKNOW_CAUSE);
			response.setErrorMessage(ex.getMessage());
			response.setErrorStack(ex);
			logger.error(LogUtils.getInstance().getFlowNo() + " runtime error", ex);
		}

		// contextManager.clearContext();

		logger.info(LogUtils.getInstance().getFlowNo() + " request for bus server spent "
				+ (System.currentTimeMillis() - now) + " ms.");
		LogUtils.getInstance().clearFlowNo();
		return response;
	}

	private static class ClientBase extends FilterBase {
		private Router router;
		private ClientConfig config;

		private ClientBase() {
			this.config = new ClientConfig();
			this.router = new DefaultRouter(config);
		}

		@Override
		public void execute(Request request, Response response) {
			String nodeId = router.route(request.getTx(), request.getVersion());
			if (nodeId == null)
				throw new BusRuntimeException(Constants.SystemError.ROUTE_NOT_FOUND,
						"Client can not find node for transaction " + request.getTx() + "(" + request.getVersion()
								+ ")" + ", please check config in bus config (" + ClientConfig.getConfigFileName()
								+ ") or contact administrator.");

			logger.info(LogUtils.getInstance().getFlowNo() + " route to node " + nodeId + ".");
			Connection connection = ConnectionFactory.getInstance().getConnection(config.getNodeBean(nodeId));
			if (connection == null) { throw new BusRuntimeException(Constants.UserError.NODE_NOT_FOUND,
					"Cannot found connection for node " + nodeId + ", please check node segments in server config."); }
			logger.info(LogUtils.getInstance().getFlowNo() + " begin remote bus Server request.");
			request.setCurrentFlowNo(LogUtils.getInstance().getFlowNo());
			response.copyFrom(connection.send(request));
		}

	}
}